package com.leiqichao.backend.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leiqichao.backend.entity.Category;
import com.leiqichao.backend.mapper.CategoryMapper;
import com.leiqichao.backend.service.CategoryService;
import jakarta.annotation.PostConstruct;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    private final CategoryMapper categoryMapper;

    public CategoryServiceImpl(CategoryMapper categoryMapper) {
        this.categoryMapper = categoryMapper;
    }


    private static final String REDIS_KEY = "categories";
    private static final String LOCK_KEY = REDIS_KEY + "_lock";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Autowired
    private RedissonClient redissonClient;

    @Override
    public List<Category> getChildren(Long parentId) {
        return categoryMapper.selectChildren(parentId == null ? 0 : parentId);
    }

    @Override
    public List<Category> getCategoryTree() {
        //从缓存中获取分类树

        return categoryMapper.selectCategoryTree();
    }

    @Override
    public boolean createCategory(Category category) {
        // 校验slug唯一性
        if (isSlugExists(category.getSlug(), null)) {
            throw new IllegalArgumentException("分类别名已存在");
        }
        return save(category);
    }

    @Override
    public boolean updateCategory(Category category) {
        // 校验slug唯一性
        if (isSlugExists(category.getSlug(), category.getId())) {
            throw new IllegalArgumentException("分类别名已存在");
        }
        return updateById(category);
    }

    /**
     * 校验分类slug唯一性
     *
     * @param slug      待校验的slug
     * @param excludeId 需要排除的ID（用于更新操作）
     */
    private boolean isSlugExists(String slug, Long excludeId) {
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Category::getSlug, slug);
        if (excludeId != null) {
            queryWrapper.ne(Category::getId, excludeId);
        }
        return count(queryWrapper) > 0;
    }

    /**
     * 启动时加载分类数据到 Redis
     */
    @PostConstruct
    public void loadCategoriesToRedis() {
        if (Boolean.TRUE.equals(redisTemplate.hasKey(REDIS_KEY))) {
            System.out.println("Redis 中已存在分类数据，跳过加载。");
            return;
        }

        List<Category> categories = loadCategoriesFromDatabaseAndCache();
        if (categories != null) {
            System.out.println("分类数据已加载到 Redis。");
        } else {
            System.out.println("加载分类数据失败。");
        }
    }

    /**
     * 获取分类数据
     * 该方法从 Redis 缓存中获取分类数据。如果缓存中没有数据，则从数据库中加载并缓存到 Redis 中。
     * 使用 Redisson 分布式锁来避免多个线程同时加载数据。
     *
     * @return 分类数据列表
     */
    public List<Category> getCategories() {
        // 从 Redis 中获取分类数据
        List<Category> categories = getCategoriesFromRedis();

        // 如果 Redis 中没有数据，则从数据库加载并缓存
        if (categories == null) {
            categories = loadCategoriesFromDatabaseAndCache();
        }

        return categories;
    }

    /**
     * 从 Redis 中获取分类数据
     *
     * @return 分类数据列表，如果 Redis 中没有数据则返回 null
     */
    private List<Category> getCategoriesFromRedis() {
        try {
            String json = redisTemplate.opsForValue().get(REDIS_KEY);
            if (json != null) {
                return JSON.parseArray(json, Category.class);
            }
        } catch (Exception e) {
            System.err.println("从 Redis 获取分类数据失败: " + e.getMessage());
        }
        return null;
    }

    /**
     * 从数据库加载分类数据并缓存到 Redis
     *
     * @return 分类数据列表，如果加载失败则返回 null
     */
    private List<Category> loadCategoriesFromDatabaseAndCache() {
        // 获取分布式锁
        RLock lock = redissonClient.getLock(LOCK_KEY);

        try {
            // 尝试加锁，最多等待 10 秒，锁的过期时间为 30 秒
            boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (isLocked) {
                // 再次检查 Redis 中是否有数据（防止其他线程已经加载）
                List<Category> categories = getCategoriesFromRedis();
                if (categories != null) {
                    return categories;
                }

                // 从数据库中查询分类数据
                categories = categoryMapper.selectList(null);

                // 缓存到 Redis 中
                cacheCategories(categories);

                return categories;
            } else {
                // 等待其他线程加载数据
                waitForDataLoad();
                return getCategories(); // 重试
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("获取分布式锁时被中断: " + e.getMessage());
            return null;
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 缓存分类数据到 Redis
     *
     * @param categories 分类数据列表
     */
    private void cacheCategories(List<Category> categories) {
        try {
            if (categories == null || categories.isEmpty()) {
                redisTemplate.opsForValue().set(REDIS_KEY, Collections.emptyList().toString(), 5, TimeUnit.MINUTES);
            } else {
                redisTemplate.opsForValue().set(REDIS_KEY, JSON.toJSONString(categories), 1, TimeUnit.HOURS);
            }
        } catch (Exception e) {
            System.err.println("缓存分类数据失败: " + e.getMessage());
        }
    }

    /**
     * 等待其他线程加载数据
     */
    private void waitForDataLoad() {
        try {
            Thread.sleep(100); // 等待 100ms
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

